use async_trait::async_trait;
use log::{error, info};
use std::fmt::Debug;

/// `Pipeline.process()` 的返回类型, 用以判断 item 是否进入下一个 pipeline 处理
pub enum ItemProduct<ItemType: Send + Sync> {
    /// 进入下个 pipeline 处理
    Item(ItemType),
    /// 不进入下个 pipeline
    Ignore,
}

#[async_trait]
pub trait Pipeline<ItemType>: Send + Sync
where
    ItemType: Send + Sync,
{
    async fn open_spider(&mut self) {}
    /// 处理 item
    async fn process(&mut self, item: ItemType) -> ItemProduct<ItemType>;
    async fn close_spider(&mut self) {}
}

#[derive(Debug)]
pub struct ConsolePipeline;

#[async_trait]
impl<ItemType> Pipeline<ItemType> for ConsolePipeline
where
    ItemType: Clone + Debug + Send + Sync + 'static,
{
    async fn process(&mut self, item: ItemType) -> ItemProduct<ItemType> {
        info!("{:?}", item);
        ItemProduct::Item(item)
    }
}

use csv;
use serde::Serialize;
#[derive(Debug)]
pub struct CsvPipeline {
    wtr: csv::Writer<std::fs::File>,
}

impl CsvPipeline {
    pub fn new(path: &str) -> Self {
        Self {
            wtr: csv::Writer::from_path(path).unwrap(),
        }
    }
}

#[async_trait]
impl<ItemType> Pipeline<ItemType> for CsvPipeline
where
    ItemType: Clone + Debug + Send + Sync + 'static + Serialize,
{
    async fn open_spider(&mut self) {
        // self.wtr = csv::Writer::from_path("data/output.csv").unwrap();
    }
    async fn process(&mut self, item: ItemType) -> ItemProduct<ItemType> {
        self.wtr.serialize(item).unwrap_or_else(|item| {
            error!("{:?} 写入失败", item);
            ()
        });
        ItemProduct::Ignore
    }
    async fn close_spider(&mut self) {}
}
